<?php
//dezend by http://www.yunlu99.com/ QQ:270656184
class CI_Form_validation
{
	/**
	 * Reference to the CodeIgniter instance
	 *
	 * @var object
	 */
	protected $CI;
	/**
	 * Validation data for the current form submission
	 *
	 * @var array
	 */
	protected $_field_data = array();
	/**
	 * Validation rules for the current form
	 *
	 * @var array
	 */
	protected $_config_rules = array();
	/**
	 * Array of validation errors
	 *
	 * @var array
	 */
	protected $_error_array = array();
	/**
	 * Array of custom error messages
	 *
	 * @var array
	 */
	protected $_error_messages = array();
	/**
	 * Start tag for error wrapping
	 *
	 * @var string
	 */
	protected $_error_prefix = '<p>';
	/**
	 * End tag for error wrapping
	 *
	 * @var string
	 */
	protected $_error_suffix = '</p>';
	/**
	 * Custom error message
	 *
	 * @var string
	 */
	protected $error_string = '';
	/**
	 * Whether the form data has been validated as safe
	 *
	 * @var bool
	 */
	protected $_safe_form_data = false;
	/**
	 * Custom data to validate
	 *
	 * @var array
	 */
	public $validation_data = array();

	public function __construct($rules = array())
	{
		$this->CI = &get_instance();

		if (isset($rules['error_prefix'])) {
			$this->_error_prefix = $rules['error_prefix'];
			unset($rules['error_prefix']);
		}

		if (isset($rules['error_suffix'])) {
			$this->_error_suffix = $rules['error_suffix'];
			unset($rules['error_suffix']);
		}

		$this->_config_rules = $rules;
		$this->CI->load->helper('form');
		log_message('info', 'Form Validation Class Initialized');
	}

	public function set_rules($field, $label = '', $rules = array(), $errors = array())
	{
		if (($this->CI->input->method() !== 'post') && empty($this->validation_data)) {
			return $this;
		}

		if (is_array($field)) {
			foreach ($field as $row) {
				if (!(isset($row['field']) && isset($row['rules']))) {
					continue;
				}

				$label = (isset($row['label']) ? $row['label'] : $row['field']);
				$errors = (isset($row['errors']) && is_array($row['errors']) ? $row['errors'] : array());
				$this->set_rules($row['field'], $label, $row['rules'], $errors);
			}

			return $this;
		}

		if (!is_string($field) || ($field === '') || empty($rules)) {
			return $this;
		}
		else if (!is_array($rules)) {
			if (!is_string($rules)) {
				return $this;
			}

			$rules = preg_split('/\\|(?![^\\[]*\\])/', $rules);
		}

		$label = ($label === '' ? $field : $label);
		$indexes = array();

		if (($is_array = (bool) preg_match_all('/\\[(.*?)\\]/', $field, $matches)) === true) {
			sscanf($field, '%[^[][', $indexes[0]);
			$i = 0;

			for ($c = count($matches[0]); $i < $c; $i++) {
				if ($matches[1][$i] !== '') {
					$indexes[] = $matches[1][$i];
				}
			}
		}

		$this->_field_data[$field] = array('field' => $field, 'label' => $label, 'rules' => $rules, 'errors' => $errors, 'is_array' => $is_array, 'keys' => $indexes, 'postdata' => NULL, 'error' => '');
		return $this;
	}

	public function set_data(array $data)
	{
		if (!empty($data)) {
			$this->validation_data = $data;
		}

		return $this;
	}

	public function set_message($lang, $val = '')
	{
		if (!is_array($lang)) {
			$lang = array($lang => $val);
		}

		$this->_error_messages = array_merge($this->_error_messages, $lang);
		return $this;
	}

	public function set_error_delimiters($prefix = '<p>', $suffix = '</p>')
	{
		$this->_error_prefix = $prefix;
		$this->_error_suffix = $suffix;
		return $this;
	}

	public function error($field, $prefix = '', $suffix = '')
	{
		if (empty($this->_field_data[$field]['error'])) {
			return '';
		}

		if ($prefix === '') {
			$prefix = $this->_error_prefix;
		}

		if ($suffix === '') {
			$suffix = $this->_error_suffix;
		}

		return $prefix . $this->_field_data[$field]['error'] . $suffix;
	}

	public function error_array()
	{
		return $this->_error_array;
	}

	public function error_string($prefix = '', $suffix = '')
	{
		if (count($this->_error_array) === 0) {
			return '';
		}

		if ($prefix === '') {
			$prefix = $this->_error_prefix;
		}

		if ($suffix === '') {
			$suffix = $this->_error_suffix;
		}

		$str = '';

		foreach ($this->_error_array as $val) {
			if ($val !== '') {
				$str .= $prefix . $val . $suffix . "\n";
			}
		}

		return $str;
	}

	public function run($group = '')
	{
		$validation_array = (empty($this->validation_data) ? $_POST : $this->validation_data);

		if (count($this->_field_data) === 0) {
			if (count($this->_config_rules) === 0) {
				return false;
			}

			if (empty($group)) {
				$group = trim($this->CI->uri->ruri_string(), '/');
				isset($this->_config_rules[$group]) || ($group = $this->CI->router->class . '/' . $this->CI->router->method);
			}

			$this->set_rules(isset($this->_config_rules[$group]) ? $this->_config_rules[$group] : $this->_config_rules);

			if (count($this->_field_data) === 0) {
				log_message('debug', 'Unable to find validation rules');
				return false;
			}
		}

		$this->CI->lang->load('form_validation');

		foreach ($this->_field_data as $field => &$row) {
			if ($row['is_array'] === true) {
				$this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']);
			}
			else if (isset($validation_array[$field])) {
				$this->_field_data[$field]['postdata'] = $validation_array[$field];
			}
		}

		foreach ($this->_field_data as $field => &$row) {
			if (empty($row['rules'])) {
				continue;
			}

			$this->_execute($row, $row['rules'], $row['postdata']);
		}

		$total_errors = count($this->_error_array);

		if (0 < $total_errors) {
			$this->_safe_form_data = true;
		}

		empty($this->validation_data) && $this->_reset_post_array();
		return $total_errors === 0;
	}

	protected function _prepare_rules($rules)
	{
		$new_rules = array();
		$callbacks = array();

		foreach ($rules as &$rule) {
			if ($rule === 'required') {
				array_unshift($new_rules, 'required');
			}
			else {
				if (($rule === 'isset') && (empty($new_rules) || ($new_rules[0] !== 'required'))) {
					array_unshift($new_rules, 'isset');
				}
				else {
					if (is_string($rule) && (strncmp('callback_', $rule, 9) === 0)) {
						$callbacks[] = $rule;
					}
					else if (is_callable($rule)) {
						$callbacks[] = $rule;
					}
					else {
						if (is_array($rule) && isset($rule[0]) && isset($rule[1]) && is_callable($rule[1])) {
							$callbacks[] = $rule;
						}
						else {
							$new_rules[] = $rule;
						}
					}
				}
			}
		}

		return array_merge($callbacks, $new_rules);
	}

	protected function _reduce_array($array, $keys, $i = 0)
	{
		if (is_array($array) && isset($keys[$i])) {
			return isset($array[$keys[$i]]) ? $this->_reduce_array($array[$keys[$i]], $keys, $i + 1) : NULL;
		}

		return $array === '' ? NULL : $array;
	}

	protected function _reset_post_array()
	{
		foreach ($this->_field_data as $field => $row) {
			if ($row['postdata'] !== NULL) {
				if ($row['is_array'] === false) {
					isset($_POST[$field]) && $_POST[$field] = $row['postdata'];
				}
				else {
					$post_ref = &$_POST;

					if (count($row['keys']) === 1) {
						$post_ref = &$post_ref[current($row['keys'])];
					}
					else {
						foreach ($row['keys'] as $val) {
							$post_ref = &$post_ref[$val];
						}
					}

					$post_ref = $row['postdata'];
				}
			}
		}
	}

	protected function _execute($row, $rules, $postdata = NULL, $cycles = 0)
	{
		if (is_array($postdata) && !empty($postdata)) {
			foreach ($postdata as $key => $val) {
				$this->_execute($row, $rules, $val, $key);
			}

			return NULL;
		}

		$rules = $this->_prepare_rules($rules);

		foreach ($rules as $rule) {
			$_in_array = false;
			if (($row['is_array'] === true) && is_array($this->_field_data[$row['field']]['postdata'])) {
				if (!isset($this->_field_data[$row['field']]['postdata'][$cycles])) {
					continue;
				}

				$postdata = $this->_field_data[$row['field']]['postdata'][$cycles];
				$_in_array = true;
			}
			else {
				$postdata = (is_array($this->_field_data[$row['field']]['postdata']) ? NULL : $this->_field_data[$row['field']]['postdata']);
			}

			$callback = $callable = false;

			if (is_string($rule)) {
				if (strpos($rule, 'callback_') === 0) {
					$rule = substr($rule, 9);
					$callback = true;
				}
			}
			else if (is_callable($rule)) {
				$callable = true;
			}
			else {
				if (is_array($rule) && isset($rule[0]) && isset($rule[1]) && is_callable($rule[1])) {
					$callable = $rule[0];
					$rule = $rule[1];
				}
			}

			$param = false;
			if (!$callable && preg_match('/(.*?)\\[(.*)\\]/', $rule, $match)) {
				$rule = $match[1];
				$param = $match[2];
			}

			if ((($postdata === NULL) || ($postdata === '')) && ($callback === false) && ($callable === false) && !in_array($rule, array('required', 'isset', 'matches'), true)) {
				continue;
			}

			if ($callback || ($callable !== false)) {
				if ($callback) {
					if (!method_exists($this->CI, $rule)) {
						log_message('debug', 'Unable to find callback validation rule: ' . $rule);
						$result = false;
					}
					else {
						$result = $this->CI->$rule($postdata, $param);
					}
				}
				else {
					$result = (is_array($rule) ? $rule[0]->$rule[1]($postdata) : $rule($postdata));

					if ($callable !== false) {
						$rule = $callable;
					}
				}

				if ($_in_array === true) {
					$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
				}
				else {
					$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
				}
			}
			else if (!method_exists($this, $rule)) {
				if (function_exists($rule)) {
					$result = ($param !== false ? $rule($postdata, $param) : $rule($postdata));

					if ($_in_array === true) {
						$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
					}
					else {
						$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
					}
				}
				else {
					log_message('debug', 'Unable to find validation rule: ' . $rule);
					$result = false;
				}
			}
			else {
				$result = $this->$rule($postdata, $param);

				if ($_in_array === true) {
					$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
				}
				else {
					$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
				}
			}

			if ($result === false) {
				if (!is_string($rule)) {
					$line = $this->CI->lang->line('form_validation_error_message_not_set') . '(Anonymous function)';
				}
				else {
					$line = $this->_get_error_message($rule, $row['field']);
				}

				if (isset($this->_field_data[$param]) && isset($this->_field_data[$param]['label'])) {
					$param = $this->_translate_fieldname($this->_field_data[$param]['label']);
				}

				$message = $this->_build_error_msg($line, $this->_translate_fieldname($row['label']), $param);
				$this->_field_data[$row['field']]['error'] = $message;

				if (!isset($this->_error_array[$row['field']])) {
					$this->_error_array[$row['field']] = $message;
				}

				return NULL;
			}
		}
	}

	protected function _get_error_message($rule, $field)
	{
		if (isset($this->_field_data[$field]['errors'][$rule])) {
			return $this->_field_data[$field]['errors'][$rule];
		}
		else if (isset($this->_error_messages[$rule])) {
			return $this->_error_messages[$rule];
		}
		else if (false !== ($line = $this->CI->lang->line('form_validation_' . $rule))) {
			return $line;
		}
		else if (false !== ($line = $this->CI->lang->line($rule, false))) {
			return $line;
		}

		return $this->CI->lang->line('form_validation_error_message_not_set') . '(' . $rule . ')';
	}

	protected function _translate_fieldname($fieldname)
	{
		if ((sscanf($fieldname, 'lang:%s', $line) === 1) && (false === ($fieldname = $this->CI->lang->line($line, false)))) {
			return $line;
		}

		return $fieldname;
	}

	protected function _build_error_msg($line, $field = '', $param = '')
	{
		if (strpos($line, '%s') !== false) {
			return sprintf($line, $field, $param);
		}

		return str_replace(array('{field}', '{param}'), array($field, $param), $line);
	}

	public function has_rule($field)
	{
		return isset($this->_field_data[$field]);
	}

	public function set_value($field = '', $default = '')
	{
		if (!(isset($this->_field_data[$field]) && isset($this->_field_data[$field]['postdata']))) {
			return $default;
		}

		if (is_array($this->_field_data[$field]['postdata'])) {
			return array_shift($this->_field_data[$field]['postdata']);
		}

		return $this->_field_data[$field]['postdata'];
	}

	public function set_select($field = '', $value = '', $default = false)
	{
		if (!(isset($this->_field_data[$field]) && isset($this->_field_data[$field]['postdata']))) {
			return ($default === true) && (count($this->_field_data) === 0) ? ' selected="selected"' : '';
		}

		$field = $this->_field_data[$field]['postdata'];
		$value = (string) $value;

		if (is_array($field)) {
			foreach ($field as &$v) {
				if ($value === $v) {
					return ' selected="selected"';
				}
			}

			return '';
		}
		else {
			if (($field === '') || ($value === '') || ($field !== $value)) {
				return '';
			}
		}

		return ' selected="selected"';
	}

	public function set_radio($field = '', $value = '', $default = false)
	{
		if (!(isset($this->_field_data[$field]) && isset($this->_field_data[$field]['postdata']))) {
			return ($default === true) && (count($this->_field_data) === 0) ? ' checked="checked"' : '';
		}

		$field = $this->_field_data[$field]['postdata'];
		$value = (string) $value;

		if (is_array($field)) {
			foreach ($field as &$v) {
				if ($value === $v) {
					return ' checked="checked"';
				}
			}

			return '';
		}
		else {
			if (($field === '') || ($value === '') || ($field !== $value)) {
				return '';
			}
		}

		return ' checked="checked"';
	}

	public function set_checkbox($field = '', $value = '', $default = false)
	{
		return $this->set_radio($field, $value, $default);
	}

	public function required($str)
	{
		return is_array($str) ? empty($str) === false : trim($str) !== '';
	}

	public function regex_match($str, $regex)
	{
		return (bool) preg_match($regex, $str);
	}

	public function matches($str, $field)
	{
		return isset($this->_field_data[$field]) && isset($this->_field_data[$field]['postdata']) ? $str === $this->_field_data[$field]['postdata'] : false;
	}

	public function differs($str, $field)
	{
		return !(isset($this->_field_data[$field]) && ($this->_field_data[$field]['postdata'] === $str));
	}

	public function is_unique($str, $field)
	{
		sscanf($field, '%[^.].%[^.]', $table, $field);
		return isset($this->CI->db) ? $this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0 : false;
	}

	public function min_length($str, $val)
	{
		if (!is_numeric($val)) {
			return false;
		}

		return $val <= mb_strlen($str);
	}

	public function max_length($str, $val)
	{
		if (!is_numeric($val)) {
			return false;
		}

		return mb_strlen($str) <= $val;
	}

	public function exact_length($str, $val)
	{
		if (!is_numeric($val)) {
			return false;
		}

		return mb_strlen($str) === (int) $val;
	}

	public function valid_url($str)
	{
		if (empty($str)) {
			return false;
		}
		else if (preg_match('/^(?:([^:]*)\\:)?\\/\\/(.+)$/', $str, $matches)) {
			if (empty($matches[2])) {
				return false;
			}
			else if (!in_array(strtolower($matches[1]), array('http', 'https'), true)) {
				return false;
			}

			$str = $matches[2];
		}

		if (preg_match('/^\\[([^\\]]+)\\]/', $str, $matches) && !is_php('7') && (filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false)) {
			$str = 'ipv6.host' . substr($str, strlen($matches[1]) + 2);
		}

		return filter_var('http://' . $str, FILTER_VALIDATE_URL) !== false;
	}

	public function valid_email($str)
	{
		if (function_exists('idn_to_ascii') && (sscanf($str, '%[^@]@%s', $name, $domain) === 2)) {
			$str = $name . '@' . idn_to_ascii($domain);
		}

		return (bool) filter_var($str, FILTER_VALIDATE_EMAIL);
	}

	public function valid_emails($str)
	{
		if (strpos($str, ',') === false) {
			return $this->valid_email(trim($str));
		}

		foreach (explode(',', $str) as $email) {
			if ((trim($email) !== '') && ($this->valid_email(trim($email)) === false)) {
				return false;
			}
		}

		return true;
	}

	public function valid_ip($ip, $which = '')
	{
		return $this->CI->input->valid_ip($ip, $which);
	}

	public function alpha($str)
	{
		return ctype_alpha($str);
	}

	public function alpha_numeric($str)
	{
		return ctype_alnum((string) $str);
	}

	public function alpha_numeric_spaces($str)
	{
		return (bool) preg_match('/^[A-Z0-9 ]+$/i', $str);
	}

	public function alpha_dash($str)
	{
		return (bool) preg_match('/^[a-z0-9_-]+$/i', $str);
	}

	public function numeric($str)
	{
		return (bool) preg_match('/^[\\-+]?[0-9]*\\.?[0-9]+$/', $str);
	}

	public function integer($str)
	{
		return (bool) preg_match('/^[\\-+]?[0-9]+$/', $str);
	}

	public function decimal($str)
	{
		return (bool) preg_match('/^[\\-+]?[0-9]+\\.[0-9]+$/', $str);
	}

	public function greater_than($str, $min)
	{
		return is_numeric($str) ? $min < $str : false;
	}

	public function greater_than_equal_to($str, $min)
	{
		return is_numeric($str) ? $min <= $str : false;
	}

	public function less_than($str, $max)
	{
		return is_numeric($str) ? $str < $max : false;
	}

	public function less_than_equal_to($str, $max)
	{
		return is_numeric($str) ? $str <= $max : false;
	}

	public function in_list($value, $list)
	{
		return in_array($value, explode(',', $list), true);
	}

	public function is_natural($str)
	{
		return ctype_digit((string) $str);
	}

	public function is_natural_no_zero($str)
	{
		return ($str != 0) && ctype_digit((string) $str);
	}

	public function valid_base64($str)
	{
		return base64_encode(base64_decode($str)) === $str;
	}

	public function prep_for_form($data)
	{
		if (($this->_safe_form_data === false) || empty($data)) {
			return $data;
		}

		if (is_array($data)) {
			foreach ($data as $key => $val) {
				$data[$key] = $this->prep_for_form($val);
			}

			return $data;
		}

		return str_replace(array('\'', '"', '<', '>'), array('&#39;', '&quot;', '&lt;', '&gt;'), stripslashes($data));
	}

	public function prep_url($str = '')
	{
		if (($str === 'http://') || ($str === '')) {
			return '';
		}

		if ((strpos($str, 'http://') !== 0) && (strpos($str, 'https://') !== 0)) {
			return 'http://' . $str;
		}

		return $str;
	}

	public function strip_image_tags($str)
	{
		return $this->CI->security->strip_image_tags($str);
	}

	public function encode_php_tags($str)
	{
		return str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);
	}

	public function reset_validation()
	{
		$this->_field_data = array();
		$this->_error_array = array();
		$this->_error_messages = array();
		$this->error_string = '';
		return $this;
	}
}

defined('BASEPATH') || exit('No direct script access allowed');

?>
